package org.moonlight.jvm.instructions.references;

import org.moonlight.jvm.instructions.base.Index16Instruction;
import org.moonlight.jvm.rtda.exclusive.Frame;
import org.moonlight.jvm.rtda.exclusive.OperandStack;
import org.moonlight.jvm.rtda.share.heap.Class;
import org.moonlight.jvm.rtda.share.heap.Slots;
import org.moonlight.jvm.rtda.share.heap.classmember.Field;
import org.moonlight.jvm.rtda.share.heap.symref.FieldRef;
import org.moonlight.jvm.rtda.share.methodarea.RtConstantPool;

/**
 * GetStatic: getstatic指令和putstatic正好相反，它取出类的某个静态变量值，然后推入栈顶。
 *            getstatic指令只需要一个操作数：uint16常量池索引。
 *            通过这个索引可以从当前类的运行时常量池中找到一个字段符号引用，
 *            解析这个符号引用就可以得到这个字段，然后根据 {@link Field#getSlotId()}
 *            去 {@link Class#getStaticVars()} 取出对应的值，推入到操作数栈栈顶
 *
 * @author Moonlight
 * @createTime 2023/8/30 14:53
 **/
public class GetStatic extends Index16Instruction {

    @Override
    public void execute(Frame frame) {
        // 当前运行时常量池
        RtConstantPool rtCp = frame.getMethod().getClazz().getRtCp();
        // 通过 uint16索引 获取 字段符号引用
        FieldRef fieldRef = (FieldRef) rtCp.getConstant(this.idx);
        // 通过这个字段符号引用，找到对应的字段和类
        Field field = fieldRef.resolvedField();
        Class clazz = field.getClazz();
        // 如果类还没有经过类初始化，那么需要先经过类初始化
        // 类初始化时，先终止当前指令的执行，并将当前栈帧的nextPc重置指向当前指令
        if (!clazz.isInitStarted()) {
            frame.revertNextPc();
            clazz.classInit(frame.getThread());
            return;
        }
        // 如果解析后的字段是实例字段而非静态字段，则抛出IncompatibleClassChangeError异常。
        if (!field.isStatic()) {
            throw new IncompatibleClassChangeError();
        }

        String desc = field.getDescriptor();
        int slotId = field.getSlotId();
        Slots staticVars = clazz.getStaticVars();
        OperandStack operandStack = frame.getOperandStack();

        switch (desc.substring(0, 1)) {
            case "Z":
            case "B":
            case "C":
            case "S":
            case "I":
                operandStack.pushInt( staticVars.getInt(slotId) );
                break;
            case "F":
                operandStack.pushFloat( staticVars.getFloat(slotId) );
                break;
            case "J":
                operandStack.pushLong( staticVars.getLong(slotId) );
                break;
            case "D":
                operandStack.pushDouble( staticVars.getDouble(slotId) );
                break;
            case "L":
            case "[":
                operandStack.pushRef( staticVars.getRef(slotId) );
                break;
            default:
                throw new RuntimeException("不支持的描述符: " + desc);
        }
    }

}
